#pragma once
/*
 *      Copyright (C) 2005-2011 Team XBMC
 *      http://www.xbmc.org
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with XBMC; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */
#include "platform/util/StdString.h"
#include "platform/util/buffer.h"
#include "lib/utils.h"
#include "lib/device.h"
#include "PVREtpProtocol.h"
#include "client.h"
#include "libXBMC_addon.h"
//#include "libXBMC_codec.h"
#include "libXBMC_pvr.h"
#include "libXBMC_gui.h"
#include <math.h>


using namespace ADDON;

#define ETP_MAX_TIMING_COUNT				4
#define ETP_MAX_PID_COUNT					10

#define ETP_THREAD_READ						0x01
#define ETP_THREAD_PROCESS					0x02
#define ETP_THREAD_UPDATES					0x03




static const uint8_t AC3_START_CODE[2] = {0x0B, 0x77};

static const uint32_t AC3_FRAME_SIZE[38][4]=
{
	{ 64,   69,   96,  32   },
	{ 64,   70,   96,  32   },
	{ 80,   87,   120,  40  },
	{ 80,   88,   120,  40  },
	{ 96,   104,  144,  48  },
	{ 96,   105,  144,  48  },
	{ 112,  121,  168,  56  },
	{ 112,  122,  168,  56  },
	{ 128,  139,  192,  64  },
	{ 128,  140,  192,  64  },
	{ 160,  174,  240,  80  },
	{ 160,  175,  240,  80  },
	{ 192,  208,  288,  96  },
	{ 192,  209,  288,  96  },
	{ 224,  243,  336,  112 },
	{ 224,  244,  336,  112 },
	{ 256,  278,  384,  128 },
	{ 256,  279,  384,  128 },
	{ 320,  348,  480,  160 },
	{ 320,  349,  480,  160 },
	{ 384,  417,  576,  192 },
	{ 384,  418,  576,  192 },
	{ 448,  487,  672,  224 },
	{ 448,  488,  672,  224 },
	{ 512,  557,  768,  256 },
	{ 512,  558,  768,  256 },
	{ 640,  696,  960,  320 },
	{ 640,  697,  960,  320 },
	{ 768,  835,  1152, 384 },
	{ 768,  836,  1152, 384 },
	{ 896,  975,  1344, 448 },
	{ 896,  976,  1344, 448 },
	{ 1024, 1114, 1536, 512 },
	{ 1024, 1115, 1536, 512 },
	{ 1152, 1253, 1728, 576 },
	{ 1152, 1254, 1728, 576 },
	{ 1280, 1393, 1920, 640 },
	{ 1280, 1394, 1920, 640 },
};

static const uint32_t AC3_FSCOD[4]=
{
    48000,
    44100,
    32000,
    0,
};

static const double ETP_FPS_INDEX[16]=
{
    0.0,            // 0 Forbidden
    (24000.0/1001.0), // 1 (23.976 fps) - FILM
    24.0,           // 2 (24.000 fps)
    25.0,           // 3 (25.000 fps) - PAL
    (30000.0/1001.0), // 4 (29.970 fps) - NTSC
    30.0,           // 5 (30.000 fps)
    50.0,           // 6 (50.000 fps) - PAL noninterlaced
    (60000.0/1001.0), // 7 (59.940 fps) - NTSC noninterlaced
    60.0,           // 8 (60.000 fps)
    0,              // 9  Reserverd
    0,              // 10 Reserverd
    0,              // 11 Reserverd
    0,              // 12 Reserverd
    0,              // 13 Reserverd
    0,              // 14 Reserverd
    0               // 15 Reserverd
};

static const double ETP_ASPECT_INDEX[16]=
{
    0,      // 0 Forbidden
    1.0,            // 1 (1.0)
    4.0/3.0, // 2 (4:3 - 1.33)
    16.0/9.0, // 3 (16:9 - 1.77)
    2.21,           // 4 (29.970 fps) - NTSC
    0,      // 5 Reserverd
    0,      // 6 Reserverd
    0,      // 7 Reserverd
    0,      // 8 Reserverd
    0,      // 9 Reserverd
    0,      // 10 Reserverd
    0,      // 11 Reserverd
    0,      // 12 Reserverd
    0,      // 13 Reserverd
    0,      // 14 Reserverd
    0       // 15 Reserverd
};

#define GENRE_MAIN_COUNT		22
#define GENRE_SUB_COUNT			43

static const char *GENRE_SUB[GENRE_SUB_COUNT] =
{
	"Baseball",
	"Basketball",
	"Golf",
	"Football",
	"Hockey",
	"Soccer",
	"Boxing",
	"Action and Adventure",
	"Science Fiction",
	"Horror",
	"Fantasy",
	"Drama",
	"Mystery",
	"Suspense",
	"Drama",
	"Musical",
	"Music",
	"Western",
	"Romance",
	"Awards/Event",
	"Business",
	"Cooking",
	"Home",
	"Interview",
	"Racing",
	"How-to",
	"Science",
	"Tennis",
	"Travel",
	"Weather",
	"Home and Garden",
	"Game",
	"Racing",
	"Outdoor",
	"Episodic",
	"Documentary",
	"Comedy",
	"Reality",
	"Religious",
	"Public Affairs",
	"Current Events",
	"Special",
	"Other",
};

static const char *GENRE_MAIN[GENRE_MAIN_COUNT] =
{
	"News",
	"Sports",
	"Paid Programming",
	"Movie",
	"Comedy",
	"Action/Adventure",
	"Talk Show",
	"Game Show",
	"Soap Opera",
	"Miniseries",
	"Drama",
	"Music",
	"Reality",
	"Lifestyle",
	"Children",
	"Cooking",
	"Adventure",
	"Special",
	"Educational",
	"Travel",
	"How-to",
	"Other",
};

struct etp_pid_map
{
//	CodecDescriptor *codec_desc;
	double last_pts;
	uint64_t first_pts;
	uint16_t pid;
	int frame_size;
	int type;
	int codec;
	double duration;
};
typedef struct etp_pid_map ETP_PID_MAP;

struct etp_stream_timestamp_pair
{
	uint64_t pts;
	uint64_t dts;
	double duration;
};
typedef struct etp_stream_timestamp_pair ETP_STREAM_TIMESTAMP_PAIR;

struct etp_stream_buffer
{
	ETP_STREAM_TIMESTAMP_PAIR timing;
	uint8_t *buffer;
	size_t buffer_pos;
	size_t alloc_size;
};
typedef struct etp_stream_buffer ETP_STREAM_BUFFER;

struct etp_stream_packet
{
	uint64_t frame_count;
	uint16_t pid;
	uint8_t need_sync;
	uint8_t has_start;
	ETP_PID_MAP *map;
	ETP_STREAM_BUFFER stream_buffer;
};
typedef struct etp_stream_packet ETP_STREAM_PACKET;

class PVREtp
{
	public:
		bool					m_bDoDemux;

		PVREtp(CStdString, int);
		~PVREtp(void);

		int GetChannelsAmount(void);
		PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio);
		PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd);
		int GetRecordingsAmount(void);
		PVR_ERROR GetRecordings(ADDON_HANDLE handle);

		bool OpenRecordedStream(const PVR_RECORDING &recording);
		void CloseRecordedStream(void);
		int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize);
		long long SeekRecordedStream(long long iPosition, int iWhence /* = SEEK_SET */);
		long long PositionRecordedStream(void);
		long long LengthRecordedStream(void);

		bool OpenLiveStream(const PVR_CHANNEL &channel);
		int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize);
		void CloseLiveStream(void);
		long long LengthLiveStream(void);
		long long PositionLiveStream(void);
		bool CanSeekStream(void);
		bool CanPauseStream(void);
		void PauseStream(bool bPaused);
		long long SeekLiveStream(long long iPosition, int iWhence /* = SEEK_SET */);
		DemuxPacket* DemuxRead(void);
		void DemuxReset(void);
		void DemuxFlush(void);
		void DemuxAbort(void);
		bool SeekTime(int, bool, double*);
		PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES*);
		PVR_ERROR DeleteRecording(const PVR_RECORDING &recording);

		int GetTimersAmount(void);
		PVR_ERROR GetTimers(ADDON_HANDLE handle);
		PVR_ERROR AddTimer(const PVR_TIMER &timer);
		PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete);
		PVR_ERROR UpdateTimer(const PVR_TIMER &timer);


		PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus);

		time_t GetPlayingTime(void);
		time_t GetBufferTimeStart(void);
		time_t GetBufferTimeEnd(void);

		bool Ping(void);

	private:
		struct thread_opts
		{
			void *		_this;
			pthread_t	tid;
			uint8_t 	thread_type;
			bool 		inited;
//			bool 		wait;
		};
		typedef struct thread_opts THREAD_OPTS;

		void					*ProcessRead(void);
		void					*ProcessFrames(void);
		void					*UpdateInfo(void);
		static void				*thread_func(void *);

		PLATFORM::CMutex		m_ReadCloseLock;
		PVREtpProtocol			*m_ETPProto;
		PLATFORM::SyncedBuffer<DemuxPacket*> *m_demuxPacketBuffer;
		PLATFORM::SyncedBuffer<uint8_t*> *m_NetworkBuffer;
		PLATFORM::SyncedBuffer<uint8_t*> *m_MemList;

		uint16_t				m_SignalStrength;
		uint16_t				m_SignalNoiseRatio;


		uint8_t					m_TimerTriggered;
		uint8_t					m_RecTriggered;
		uint8_t					m_UseMemList;
		uint8_t					m_StreamDie;

		CStdString				m_strRemoteIP;
		int						m_iRemotePort;
		uint16_t				m_usRemoteDataPort;

		uint64_t				m_APushed;
		uint64_t				m_VPushed;
		uint64_t				m_TimerSeqNum;
		uint64_t				m_RecSeqNum;
		int						m_TimerCount;
		int						m_RecCount;
		uint64_t				m_EPGCount;
		uint64_t				m_PacketCount;
		uint64_t				m_NullPacketCount;

		uint64_t				m_EndTime;
		uint64_t				m_StartTime;
		uint64_t				m_CurrentTime;
		uint64_t				m_BufTimeOffset;

		uint32_t				m_StreamOpenWaitTime;
		time_t					m_StreamOpenTime;
		time_t					m_LastEndTimeUpdate;
		bool					m_StreamIsRecording;
		bool					m_Seeking;
		bool					m_Paused;
		bool					m_WaitForVideoFrame;
		bool					m_ForceUpdateInfo;
		uint64_t				m_LastSeekTime;

		THREAD_OPTS				m_ReadThreadOpts;
		THREAD_OPTS				m_FramesThreadOpts;
		THREAD_OPTS				m_UpdateThreadOpts;


		PVR_STREAM_PROPERTIES 	m_Props;

		uint32_t				m_TsBufferSize;
		int						m_EtpStreamCount;
		ETP_STREAM_PACKET 		m_EtpStream[PVR_STREAM_MAX_STREAMS];
		int						m_PidMapCount;
		ETP_PID_MAP				m_PidMap[PVR_STREAM_MAX_STREAMS];

		ETP_PID_MAP* 			PidGetMap(uint16_t);
		int						PidGetIndex(uint16_t);
		ETP_PID_MAP*			PidAdd(uint16_t);
		int						PidIsValid(uint16_t);
		void					PidsClear(void);
		uint64_t				GetStartTime(void);

		ETP_STREAM_PACKET		*EtpGetStream(uint16_t);
		ETP_STREAM_PACKET		*EtpAddStream(uint16_t);
		ETP_STREAM_PACKET 		*EtpAddStreamData(uint16_t, uint8_t *, int);
		ETP_STREAM_PACKET 		*EtpStreamInit(uint16_t);

		int 					EtpProcessStream(ETP_STREAM_PACKET *);
		void 					EtpResetStream(void);

		bool 					StreamOpenNext(void);
		bool 					SyncWait(uint32_t);
		bool					StreamAlive(void);

		uint8_t					m_SeekedReadACK;
		uint8_t					m_SeekedProcessACK;

		uint16_t				m_StreamFoundMsg[ETP_MAX_PID_COUNT];
		uint16_t				m_StreamProcessMsg[ETP_MAX_PID_COUNT];
		uint8_t					m_StreamProcessCount;

		uint64_t 				DVD_to_PTS(double p) { return floor((p/(double)DVD_TIME_BASE) * 90000.0);};
		double 					PTS_to_DVD(uint64_t p) { return ((double)p/90000.0) * DVD_TIME_BASE; };
		int 					GetGenreFromStr(char *, char *);
		uint32_t 				PeekAC3FrameSize(uint8_t *,  uint32_t);
		time_t					TimeNow(void) {return std::time(NULL);};

		PLATFORM::SyncedBuffer<PVR_TIMER*> *m_TimerCache;
		PLATFORM::SyncedBuffer<PVR_RECORDING*> *m_RecCache;

		PLATFORM::CMutex		m_TimerUpdateLock;
		PLATFORM::CMutex		m_RecUpdateLock;

};
